use std::ops::Not;

use modular_bitfield_msb::prelude::*;

#[bitfield]
#[derive(Debug,Clone,Copy)]
#[repr(u8)]
pub struct FrameHeaderDescriptor {
    frame_content_size_flag:FrameContentSizeFlag,
    single_segment_flag:bool,
    unused:B1,
    reversed:B1,
    content_checksum_flag:bool,
    dictionary_id_flag:DictionaryIdSizeFlag,
}

impl FrameHeaderDescriptor {
    pub fn fcs_field_size(&self) -> FcsFieldSize {
        match self.frame_content_size_flag() {
            FrameContentSizeFlag::ZeroOrOneByte => 
                if self.is_single_segment() { FcsFieldSize::U8 } else { FcsFieldSize::None },
            FrameContentSizeFlag::TwoBytes => FcsFieldSize::U16,
            FrameContentSizeFlag::FourBytes => FcsFieldSize::U32,
            FrameContentSizeFlag::EightBytes => FcsFieldSize::U64,
        }
    }

    pub fn did_field_size(&self) -> DidFieldSize {
        match self.dictionary_id_flag() {
            DictionaryIdSizeFlag::ZeroByte => DidFieldSize::None,
            DictionaryIdSizeFlag::OneByte => DidFieldSize::U8,
            DictionaryIdSizeFlag::TwoBytes => DidFieldSize::U16,
            DictionaryIdSizeFlag::FourBytes => DidFieldSize::U32,
        }
    }

    pub fn modify_single_segment_flag(&mut self,val:bool) {
        self.set_single_segment_flag(val);
    }

    pub fn modify_content_checksum_flag(&mut self,val:bool) {
        self.set_content_checksum_flag(val);
    }

    pub fn modify_frame_content_size_flag(&mut self,val:u64) {
        let bit_num = 64 - val.leading_zeros();
        if (0..9).contains(&bit_num) {
            self.set_frame_content_size_flag(FrameContentSizeFlag::ZeroOrOneByte);
        } else if (9..17).contains(&bit_num) {
            self.set_frame_content_size_flag(FrameContentSizeFlag::TwoBytes);
        } else if (17..33).contains(&bit_num) {
            self.set_frame_content_size_flag(FrameContentSizeFlag::FourBytes);
        } else if (33..65).contains(&bit_num) {
            self.set_frame_content_size_flag(FrameContentSizeFlag::EightBytes);
        }
    }

    pub fn modify_dictionary_id_flag(&mut self,val:Option<u32>) {
        if val.is_none() { 
            self.set_dictionary_id_flag(DictionaryIdSizeFlag::ZeroByte);
         }
        let bit_num = 64 - val.unwrap().leading_zeros();
        if (0..9).contains(&bit_num) {
            self.set_dictionary_id_flag(DictionaryIdSizeFlag::OneByte);
        } else if (9..17).contains(&bit_num) {
            self.set_dictionary_id_flag(DictionaryIdSizeFlag::TwoBytes);
        } else if (17..33).contains(&bit_num) {
            self.set_dictionary_id_flag(DictionaryIdSizeFlag::FourBytes);
        }
    }

    pub fn is_single_segment(&self) -> bool {
        self.single_segment_flag()
    }

    pub fn contain_window_descriptor(&self) -> bool {
        self.single_segment_flag().not()
    }

    pub fn contain_content_checksum(&self) -> bool {
        self.content_checksum_flag()
    }

    pub fn useless(&self) {
        self.unused();
        self.reversed();
    }
}

#[derive(BitfieldSpecifier,Debug)]
#[bits = 2]
pub enum FrameContentSizeFlag {
    ZeroOrOneByte = 0,
    TwoBytes = 1,
    FourBytes = 2,
    EightBytes = 3,
}

#[derive(BitfieldSpecifier,Debug)]
#[bits = 2]
pub enum DictionaryIdSizeFlag {
    ZeroByte = 0,
    OneByte = 1,
    TwoBytes = 2,
    FourBytes = 3,
}

pub enum FcsFieldSize {
    None,
    U8,
    U16,
    U32,
    U64,
}

pub enum DidFieldSize {
    None,
    U8,
    U16,
    U32,
}

